#!/usr/bin/python3

import sys

#i love aocd.

from aocd import get_data
from aocd import submit

### SHORTCUTS

def intify(str_list):
    """
    Takes a list of stings that are assumed to refer to integers and returns a list of integers.
    """

    return [int(item) for item in str_list]

### REFS

values = { # i'm not sorry for this at all.
        'a':1,
        'b':2,
        'c':3,
        'd':4,
        'e':5,
        'f':6,
        'g':7,
        'h':8,
        'i':9,
        'j':10,
        'k':11,
        'l':12,
        'm':13,
        'n':14,
        'o':15,
        'p':16,
        'q':17,
        'r':18,
        's':19,
        't':20,
        'u':21,
        'v':22,
        'w':23,
        'x':24,
        'y':25,
        'z':26,
        'A':27,
        'B':28,
        'C':29,
        'D':30,
        'E':31,
        'F':32,
        'G':33,
        'H':34,
        'I':35,
        'J':36,
        'K':37,
        'L':38,
        'M':39,
        'N':40,
        'O':41,
        'P':42,
        'Q':43,
        'R':44,
        'S':45,
        'T':46,
        'U':47,
        'V':48,
        'W':49,
        'X':50,
        'Y':51,
        'Z':52,
        }

### DAYS

def day_one_solver():
    """
    Returns the answer for day 1.
    """

    loads = day_one_processor(get_data(day=1, year=2022).split("\n"))
    p1 = loads[-1]
    p2 = loads[-1] + loads[-2] + loads[-3]

    return p1, p2

def day_one_processor(input):
    """
    Returns an int list of summed up pack loads.
    """

    loads = []
    load = 0

    for x in input:
        if x:
            load = load + int(x)
        else:
            loads.append(load)
            load = 0

    loads.sort()

    return loads

def day_two_solver():
    """
    Returns the answer for day 2.
    """

    sample = ["A Y", "B X", "C Z"]
    data = get_data(day=2, year=2022).split("\n")

    return day_two_processor_a(data), day_two_processor_b(data)

def day_two_processor_a(input):
    """
    Day 2 part A processor. I'm sure there's a tidier way to write this, but I
    did this fast.
    """

    score = 0

    for x in input:
        (elf, you)= x.split()
        if you == "X":
            score = score + 1
            if elf == "C":
                score = score + 6
            elif elf == "A":
                score = score + 3

        elif you == "Y":
            score = score + 2
            if elf == "A":
                score = score + 6
            elif elf == "B":
                score = score + 3

        else:
            score = score + 3
            if elf == "B":
                score = score + 6
            elif elf == "C":
                score = score + 3

    return score

def day_two_processor_b(input):
    """
    Day 2 part B processor; yeah, naive.
    """

    score = 0

    for x in input:
        (elf, you)= x.split()
        if you == "X":
            if elf == "C":
                score = score + 2
            elif elf == "A":
                score = score + 3
            else:
                score = score + 1

        elif you == "Y":
            score = score + 3
            if elf == "A":
                score = score + 1
            elif elf == "B":
                score = score + 2
            else:
                score = score + 3

        else:
            score = score + 6
            if elf == "B":
                score = score + 3
            elif elf == "C":
                score = score + 1
            else:
                score = score + 2

    return score

def day_three_solver():
    """
    Returns the answer for day 3.
    """

    sample = ["vJrwpWtwJgWrhcsFMMfFFhFp",
    "jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL",
    "PmmdzqPrVvPwwTWBwg",
    "wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn",
    "ttgJtRGJQctTZtZT",
    "CrZsJsPPZsGzwwsLwLmpwMDw"]

    data = get_data(day=3, year=2022).split("\n")

    p1 = day_three_processor_a(data)
    p2 = day_three_processor_b(data)

    return p1, p2

def day_three_processor_a(input):

    score = 0

    for x in input:
        dup = ""
        items = list(x)

        half = int(len(items)/2)
        for y in items[0:half]:
            if y in items[half:]:
                dup = y

        score = score + values.get(dup)

    return score

def day_three_processor_b(input):
    """
    weeee iterating
    """

    score = 0
    index = 0
    count = 3

    for x in input:
        if count % 3 == 0:
            dup = ""
            items = list(x)

            for y in items:
                if y in list(input[index+1]) and y in list(input[index+2]):
                    dup = y

            print(dup)
            score = score + values.get(dup)
            index = index + 1
            count = count + 1
        else:
            index = index + 1
            count = count + 1
            continue

    return score

def day_four_solver():
    """
    Returns the answer for day 4.
    """

    sample = ["2-4,6-8", "2-3,4-5", "5-7,7-9", "2-8,3-7", "6-6,4-6", "2-6,4-8"]
    data = get_data(day=4, year=2022).split("\n")

    p1 = day_four_processor_a(data)
    p2 = day_four_processor_b(data)

    return p1, p2

def day_four_processor_a(input):
    """
    do you know how long i took me to realize i forgot to intify and was
    comparing strings, python, is very dirty
    """

    ans = 0

    for pair in input:
        pairs = pair.split(",")
        a = intify(pairs[0].split("-"))
        b = intify(pairs[1].split("-"))

        if a[0] >= b[0] and a[0] <= b[1]:
            if a[1] <= b[1]:
                ans = ans +1
            elif a[0] == b[0]:
                if b[1] <= a[1]:
                    ans = ans +1

        elif b[0] >= a[0] and b[0] <= a[1]:
                if b[1] <= a[1]:
                    ans = ans +1

    return ans

def day_four_processor_b(input):
    """
    wow this took me a lot of staring before i realized it was totally trivial.
    """

    ans = 0

    for pair in input:
        pairs = pair.split(",")
        a = intify(pairs[0].split("-"))
        b = intify(pairs[1].split("-"))

        if a[0] >= b[0] and a[0] <= b[1]:
            ans = ans +1

        elif b[0] >= a[0] and b[0] <= a[1]:
            ans = ans +1

    return ans

def day_five_solver():
    """
    today i was like, fuck it, i'm not parsing that shit, and hand-typed in all
    the stacks. this means i cannot trivially repeat this with other input
    without looking at it and hand-typing and hardcoding in slices.

    one of those days where implementing part b is actually more
    straightforward for me than part a, but probably because i did all the work
    in a way that made it easily reused.

    did a couple passes on tidying up the code after submission. i could do the
    honorable thing and write out a full input parser for my own health, but
    also, the star awarding doesn't care how you get the right answer, and i
    have only so many minutes in my life.
    """

    s_data = """    [D]
[N] [C]
[Z] [M] [P]
 1   2   3

move 1 from 2 to 1
move 3 from 1 to 3
move 2 from 2 to 1
move 1 from 1 to 2""".split("\n")[5:]
    s_stack = ["ZN", "MCD", "P"]

    data = get_data(day=5, year=2022).split("\n")[10:]
    stack = ["VCDRZGBW", "GWFCBSTV", "CBSNW", "QGMNJVCP", "TSLFDHB", "JVTWMN",
            "PFLCSTG", "BDZ", "MNZW"]

    p1, p2 = "",""

    codes = get_instructions(data)

    for x in codes: # instructions are 1-indexed
        stack = crane_it_a(x[0], x[1]-1, x[2]-1, stack)

    for x in stack:
        p1 += x[-1]

    ## reset the stack for part b WAIT WHY DO I SOMETIMES SAY PART B AND
    ## SOMETIMES SAY P2
    stack = ["VCDRZGBW", "GWFCBSTV", "CBSNW", "QGMNJVCP", "TSLFDHB", "JVTWMN",
            "PFLCSTG", "BDZ", "MNZW"]

    for x in codes: # lol watch your off-by-ones
        stack = crane_it_b(x[0]-1, x[1]-1, x[2]-1, stack)

    for x in stack:
        p2 += x[-1]

    return p1, p2

def get_instructions(data):
    """
    just parsing out the instructions and returning a list of integers
    """

    codes = []

    for line in data:
        ins = line.split(" ")
        codes.append(intify([ins[1], ins[3], ins[5]]))

    return codes

def crane_it_a(reps, start, end, stack):
    """
    im still so proud of myself for knowing how to do a good recurse!!!
    """

    if reps > 0:
        stack[end] += stack[start][-1]
        stack[start] = stack[start][0:-1]

        return crane_it_a(reps-1, start, end, stack)

    return stack

def crane_it_b(reps, start, end, stack):
    """
    so boring no recusion
    """

    stack[end] += stack[start][-1-reps:]
    stack[start] = stack[start][0:-1-reps]

    return stack

def day_six_solver():
    """
    wow this was....very easy???? had to swat some typos for p2 because i'm
    sreepy.

    did a quick pass to tidy up the processor.
    """

    s_data = "mjqjpqmgbljsphdztnvjfqwrcgsmlb"
    s_data2 = "bvwbjplbgvbhsrlpgdmjqwftvncz"

    data = get_data(day=6, year=2022)

    p1, p2 = "",""

    p1 = day_six_processor(data, 4)
    p2 = day_six_processor(data, 14)

    return p1, p2

def day_six_processor(input, length):

    ans = 0

    a = 0
    b = length

    for i in range (len(input)-length-1):
        chunk = input[a:b]
        if len(set(chunk)) == length:
            return i+length
        a += 1
        b += 1

    return ans

def day_seven_solver():
    """
    oh my god parser hell

    i got stuck on p2 all day because i was returning the name of the directory
    and not the size. oops.

    did a little cleanup post-star.
    """

    s_data = """$ cd /
$ ls
dir a
14848514 b.txt
8504156 c.dat
dir d
$ cd a
$ ls
dir e
29116 f
2557 g
62596 h.lst
$ cd e
$ ls
584 i
$ cd ..
$ cd ..
$ cd d
$ ls
4060174 j
8033020 d.log
5626152 d.ext
7214296 k""".split("\n")

    data = get_data(day=7, year=2022).split("\n")

    p1, p2 = "",""

    root, all_dirs = day_seven_parser(data)

    p1 = day_seven_processor_a(all_dirs)
    p2 = day_seven_processor_b(root, all_dirs)

    return p1, p2

class Directory:
    def __init__(self, name):
        self.name = name
        self.dirs = {}
        self.size = 0

    def add_file(self, filesize):
        self.size += filesize

    def add_dir(self, dir_name, dir):
        self.dirs.update({dir_name: dir})

    def get_size(self):
        return self.size

    def get_name(self):
        return self.name

    def get_dir(self, dir_name):
        return self.dirs.get(dir_name)

def day_seven_parser(input):
    home = Directory("root")
    here = home
    last = []
    all_dirs = []

    for i in range(len(input) - 1):
        line = input[i]

        if line[0] == "$":
            cmd = line.split(" ")[1:]
            if cmd[0] == "ls":
                ls = []

                ## slurp up all the ls output
                for j in range(i+1, len(input)):
                    if input[j][0] != "$":
                        ls.append(input[j])
                    else:
                        break

                for item in ls:
                    if item[0] == 'd':
                        dir = item.split(" ")[1]
                        new = Directory(dir)
                        here.add_dir(dir, new)
                        all_dirs.append(new)
                    else:
                        filesize = int(item.split(" ")[0])
                        here.add_file(filesize)
                        for parent in last:
                            parent.add_file(filesize)

            elif cmd[0] == "cd":
                if cmd[1] == "..":
                    here = last.pop()
                elif cmd[1] == "/":
                    here = home
                    last = []
                else:
                    last.append(here)
                    here = here.get_dir(cmd[1])

    return home, all_dirs

def day_seven_processor_a(all_dirs):

    ans = 0

    for x in all_dirs:
        a = x.get_size()
        if x.get_size() <= 100000:
            ans += x.get_size()

    return ans

def day_seven_processor_b(root, all_dirs):

    disk   = 70000000
    needed = 30000000

    free = disk - root.get_size()
    delta = needed - free
    curr = disk

    for x in all_dirs:
        a = x.get_size()

        if a >= delta:
            if a < curr:
                curr = a

    return curr

def day_eight_solver():
    """
    whew finicky matrix shit.
    """

    s_data = """30373
25512
65332
33549
35390""".split("\n")

    data = get_data(day=8, year=2022).split("\n")

    p1, p2 = "",""

    p1 = day_eight_processor_a(data)
    p2 = day_eight_processor_b(data)

    return p1, p2

def day_eight_processor_a(data):

    grid = []

    for line in data:
        grid.append(intify(list(line)))

    visible = len(grid[0]) * 2 + len(grid) * 2 - 4

    for row in range(1, len(grid[0])-1):
        x = list(grid[row])
        for column in range(1, len(x)-1):
            y = list(x)[column]
            if is_visible(row, column, grid):
                visible += 1

    return visible

def is_visible(row, column, grid):

    this = grid[row][column]
    ans = False

    for i in range(0, row):
        if grid[i][column] >= this:
            ans = False
            break
        else:
            ans = True

    if ans:
        return True

    for i in range(row+1, len(grid[row])):
        if grid[i][column] >= this:
            ans = False
            break
        else:
            ans = True

    if ans:
        return True
    for i in range(0, column):
        if grid[row][i] >= this:
            ans = False
            break
        else:
            ans = True

    if ans:
        return True

    for i in range(column+1, len(grid)):
        if grid[row][i] >= this:
            ans = False
            break
        else:
            ans = True

    return ans

def day_eight_processor_b(data):
    grid = []

    for line in data:
        grid.append(intify(list(line)))

    best = 0

    for row in range(1, len(grid[0])-1):
        x = list(grid[row])
        for column in range(1, len(x)-1):
            y = list(x)[column]
            score = calc_score(row, column, grid)
            if score > best:
                best = score

    return best

def calc_score(row, column, grid):

    this = grid[row][column]

    (u, d, l, r) = 0, 0, 0, 0,

    for i in range(row-1, -1, -1): # UP
        u += 1
        if grid[i][column] >= this:
            break

    for i in range(row+1, len(grid[row])): # DOWN
        d += 1
        if grid[i][column] >= this:
            break

    for i in range(column-1, -1, -1): # LEFT
        l += 1
        if grid[row][i] >= this:
            break

    for i in range(column+1, len(grid)): # RIGHT
        r += 1
        if grid[row][i] >= this:
            break

    return u * d * l * r

def day_nine_solver():
    """
    """

    s_data = """R 4
U 4
L 3
D 1
R 4
D 1
L 5
R 2""".split("\n")

    data = get_data(day=9, year=2022).split("\n")

    p1, p2 = "",""

    p1 = day_nine_processor_a(data)
    #p2 = day_nine_processor_b(data)

    return p1, p2

def day_nine_processor_a(data):

    visited = []

    H = (0,0)
    T = (0,0)

    for inst in data:
        print(inst)
        dir, steps = inst.split(" ")

        ## process head movement
        for x in range(0, int(steps)):
            if dir == "U":
                H = (H[0]+1, H[1])
            if dir == "D":
                H = (H[0]-1, H[1])
            if dir == "L":
                H = (H[0], H[1]-1)
            if dir == "R":
                H = (H[0], H[1]+1)

            print("before scoots")
            print("H: {}".format(H))
            print("T: {}".format(T))

            ## process tail movement
            if T not in visited:
                visited.append(T)

            if T == H:
                print("overlap")
                continue
            elif T[0] == H[0] and abs(T[1] - H[1]) == 1:
                print("adjacent")
                continue
            elif T[1] == H[1] and abs(T[0] - H[0]) == 1:
                print("adjacent")
                continue
            elif abs(T[0] - H[0]) == 1 and abs(T[1] - H[1]) == 1:
                print("kitty")
            elif T[0] == H[0]:
                ## LR scoots
                print("LR scoot")
                if H[1] > T[1]:
                    ## scoot right
                    T = (T[0], T[1] + 1)
                else:
                    ## scoot left
                    T = (T[0], T[1] - 1)
            elif T[1] == H[1]:
                ## UD scoots
                print("UD scoot")
                if H[0] > T[0]:
                    ## scoot up
                    T = (T[0] + 1, T[1])
                else:
                    ## scoot down
                    T = (T[0] - 1, T[1])
            else:
                ## diagonal scoots
                print("diagonal scoot")
                 
                if (H[0] - T[0]) > 1:
                    if H[1] > T[1]:
                        ## scoot UR
                        T = (T[0] + 1, T[1] + 1)
                    else:
                        ## scoot DR
                        T = (T[0] + 1, T[1] - 1)
                elif (H[0] - T [0]) < 1:
                    if H[1] > T[1]:
                        ## scoot UL
                        T = (T[0] - 1, T[1] + 1)
                    else:
                        ## scoot DL
                        T = (T[0] - 1, T[1] - 1)
                elif (H[1] - T [1]) > 1:
                    if H[0] > T[0]:
                        ## scoot UR
                        T = (T[0] + 1, T[1] + 1)
                    else:
                        ## scoot UL
                        T = (T[0] - 1, T[1] + 1)
                elif (H[1] - T [1]) < 1:
                    if H[0] > T[0]:
                        ## scoot DR
                        T = (T[0] + 1, T[1] - 1)
                    else:
                        ## scoot DL
                        T = (T[0] - 1, T[1] - 1)

            print("after scoots")
            print("H: {}".format(H))
            print("T: {}".format(T))

    print(visited)
    return len(visited)

def day_nine_processor_b(data):

    return 0

if __name__ == '__main__':
    #day1 = day_one_solver()
    #print("day 01: {}".format(day1))
    #submit(day1[0], part="a", day=1, year=2022)
    #submit(day1[1], part="b", day=1, year=2022)

    #day2 = day_two_solver()
    #print("day 02: {}".format(day2))
    #submit(day2[0], part="a", day=2, year=2022)
    #submit(day2[1], part="b", day=2, year=2022)

    #day3 = day_three_solver()
    #print("day 03: {}".format(day3))
    #submit(day3[0], part="a", day=3, year=2022)
    #submit(day3[1], part="b", day=3, year=2022)

    #day4 = day_four_solver()
    #print("day 04: {}".format(day4))
    #submit(day4[0], part="a", day=4, year=2022)
    #submit(day4[1], part="b", day=4, year=2022)

    #day5 = day_five_solver()
    #print("day 05: {}".format(day5))
    #submit(day5[0], part="a", day=5, year=2022)
    #submit(day5[1], part="b", day=5, year=2022)

    #day6 = day_six_solver()
    #print("day 06: {}".format(day6))
    #submit(day6[0], part="a", day=6, year=2022)
    #submit(day6[1], part="b", day=6, year=2022)

    #day7 = day_seven_solver()
    #print("day 07: {}".format(day7))
    #submit(day7[0], part="a", day=7, year=2022)
    #submit(day7[1], part="b", day=7, year=2022)

    #day8 = day_eight_solver()
    #print("day 08: {}".format(day8))
    #submit(day8[0], part="a", day=8, year=2022)
    #submit(day8[1], part="b", day=8, year=2022)

    day9 = day_nine_solver()
    print("day 09: {}".format(day9))
    submit(day9[0], part="a", day=9, year=2022)
    #submit(day9[1], part="b", day=9, year=2022)
